/**
 * File: CurtainContextImpl.java
 * Created by: mhaimel
 * Created on: 6 Jul 2009
 * CVS:  $Id: CurtainContextImpl.java,v 1.6 2009/12/07 09:36:57 mhaimel Exp $
 */
package uk.ac.ebi.curtain;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.LogFactory;

import uk.ac.ebi.curtain.configuration.CurtainInputArguments;
import uk.ac.ebi.curtain.configuration.FileLocations;
import uk.ac.ebi.curtain.configuration.impl.FileLocationsImpl;
import uk.ac.ebi.curtain.model.graph.curtain.CurtainGraph;
import uk.ac.ebi.curtain.service.GlobalFileInfoIdx;
import uk.ac.ebi.curtain.service.IdentifierFilepositionIndex;
import uk.ac.ebi.curtain.service.IdentifierStore;
import uk.ac.ebi.curtain.service.impl.IdentifierFilepositionIndexImpl;
import uk.ac.ebi.curtain.service.impl.SplitArrayStore;
import uk.ac.ebi.curtain.util.file.FileAccessService;
import uk.ac.ebi.curtain.utils.CurtainUncheckedException;
import uk.ac.ebi.curtain.utils.concurrent.UtilsCollect;
import uk.ac.ebi.curtain.utils.io.impl.FileIO;
import uk.ac.ebi.velvet.model.ContigData;
import uk.ac.ebi.velvet.model.ReadData;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;

/**
 * @author mhaimel
 *
 */
@Singleton
	
public class CurtainContextImpl implements CurtainContext {
	private final AtomicInteger curtainIteration = new AtomicInteger(1);
	private final ConcurrentMap<String, Object> map = UtilsCollect.newConcurrentMap();
	private final AtomicReference<CurtainGraph> curtainGraph = new AtomicReference<CurtainGraph>();
	private final AtomicReference<Injector> injector = new AtomicReference<Injector>();
	private final AtomicReference<CurtainInputArguments> arguments = new AtomicReference<CurtainInputArguments>();
	private final AtomicReference<GlobalFileInfoIdx> fileIndex = new AtomicReference<GlobalFileInfoIdx>();
	private final AtomicReference<IdentifierStore<String, Integer>> contigIndex = new AtomicReference<IdentifierStore<String,Integer>>();
	private final AtomicReference<IdentifierStore<String, Integer>> readIndex = new AtomicReference<IdentifierStore<String,Integer>>();
	private final AtomicReference<IdentifierStore<Integer, ContigData>> contigData = new AtomicReference<IdentifierStore<Integer,ContigData>>();
	private final AtomicReference<IdentifierStore<Integer, ReadData>> readData = new AtomicReference<IdentifierStore<Integer,ReadData>>();
	private final AtomicReference<FileAccessService> fileAccess = new AtomicReference<FileAccessService>();
	
	public CurtainContextImpl(Integer iteration){
		this.curtainIteration.set(iteration);
		/* no nothing */
		registerShutdownhook();
	}
	
	private void registerShutdownhook() {
		Runtime.getRuntime().addShutdownHook(new Thread() {
		    public void run() { 
		    	_doShutdown();
		    }
		});
	}

	private void _doShutdown(){
		if(null != fileAccess.get()){
			try{
				fileAccess.get().closeAll();
			}catch (RuntimeException e) {
				LogFactory.getLog(this.getClass()).error("Problems closing File access: ",e);
			}
		}
		if(null != fPosIdx){
			List<File> fileList = new ArrayList<File>(fPosIdx.keySet());
			for(File f : fileList){
				FileIO.closeQuietly(fPosIdx.remove(f));
			}
		}
	}

	public void setInjector(Injector injector) {
		this.injector.set(injector);
	}

	public Injector getInjector() {
		return injector.get();
	}

	public void setArguments(CurtainInputArguments arguments) {
		this.arguments.set(arguments);
	}

	public CurtainInputArguments getArguments() {
		return this.arguments.get();
	}

	public GlobalFileInfoIdx getGlobalFileIndex() {
		return this.fileIndex.get();
	}
	
	@Inject
	public void setFileIndex(GlobalFileInfoIdx fileIndex) {
		this.fileIndex.set(fileIndex);
	}

	public IdentifierStore<String, Integer> getContigIndex() {
		return contigIndex.get();
	}

	@Inject
	public void setContigIndex(IdentifierStore<String, Integer> contigIndex) {
		this.contigIndex.set(contigIndex);
	}

	public IdentifierStore<String, Integer> getReadIndex() {
		return readIndex.get();
	}

	@Inject
	public void setReadIndex(IdentifierStore<String, Integer> readIndex) {
		this.readIndex.set(readIndex);
	}

	public IdentifierStore<Integer, ContigData> getContigData() {
		if(null == contigData.get()){
			contigData.set(new SplitArrayStore<ContigData>());
		}
		return contigData.get();
	}

	public IdentifierStore<Integer, ReadData> getReadData() {
		if(null == readData.get()){
			readData.set(new SplitArrayStore<ReadData>());
		}
		return readData.get();
	}
	
	private final Map<File, IdentifierFilepositionIndex> fPosIdx = UtilsCollect.newConcurrentMap();

	private File currentWorkingDir() {
		return new File(getWorkingDir(),"curtain_"+curtainIteration.get());
	}
	
	private File getWorkingDir(){
		if(null != getArguments() 
				&& null != getArguments().getWorkingDir()){
			return getArguments().getWorkingDir();
		} else {
			throw new CurtainUncheckedException(
					"No Arguments provided for Working directory!");
		}
	}
	
	public IdentifierFilepositionIndex getPositionIndex(File file) {
		synchronized (fPosIdx) {			
			IdentifierFilepositionIndex idx = fPosIdx.get(file);
			if(null == idx){
				idx = new IdentifierFilepositionIndexImpl(file);
				fPosIdx.put(file, idx);
			}
			return idx;
		}
	}

	public FileLocations currentFiles() {
		return new FileLocationsImpl(currentWorkingDir(), getArguments());
	}

	@Inject
	public void setFileAccess(FileAccessService fileAccess) {
		this.fileAccess.set(fileAccess);
	}
	
	public FileAccessService getFileAccess() {
		return fileAccess.get();
	}

	@Override
	public CurtainGraph getGraph() {
		return this.curtainGraph.get();
	}

	@Override
	public void setGraph(CurtainGraph graph) {
		this.curtainGraph.set(graph);
	}

	@Override
	public ConcurrentMap<String, Object> getScratchBoard() {
		return map;
	}
	
}
